import io
import pickle
import socket
import struct
import threading
import time
import tkinter as tk
import zlib
from datetime import datetime
from tkinter import scrolledtext, messagebox

import ttkbootstrap as ttk
from PIL import ImageGrab, ImageTk, Image, ImageChops
from pynput import mouse, keyboard
from pynput.keyboard import Controller as KeyboardController
from pynput.mouse import Controller as MouseController
from ttkbootstrap.constants import *

from app.util.annotation_util import ClassInfo, annotate
from app.view.base_view import View


@annotate(ClassInfo(name="远程桌面", category="tool"))
class RemoteDesktopView(View):
    """设置视图"""

    def __init__(self, parent, controller):
        super().__init__(parent, controller)
        # 确保中文显示正常
        self.style = ttk.Style()

        # 核心变量
        self.role = tk.StringVar(value="server")  # server 或 client
        self.is_running = False
        self.is_connected = False
        self.is_fullscreen = False
        self.server_socket = None
        self.client_socket = None
        self.connection_thread = None
        self.screen_thread = None
        self.host = "0.0.0.0"
        self.port = 5555
        self.remote_ip = tk.StringVar(value="127.0.0.1")
        self.remote_port = tk.IntVar(value=5555)

        # 图像质量和刷新率控制
        self.quality_level = tk.IntVar(value=60)  # 图像质量 0-100
        self.max_fps = tk.IntVar(value=15)  # 最大帧率

        # 输入控制器
        self.mouse = MouseController()
        self.keyboard = KeyboardController()

        # 远程屏幕数据
        self.remote_screen = None
        self.screen_width = 0
        self.screen_height = 0
        self.last_screen = None  # 用于增量更新
        self.scroll_x = 0
        self.scroll_y = 0

        # 创建UI
        self._create_widgets()

        # 绑定全屏切换事件
        self.bind("<F11>", self.toggle_fullscreen)
        self.bind("<Escape>", self.exit_fullscreen)

    def _create_widgets(self):
        # 创建主容器
        self.main_container = ttk.Frame(self, padding=10)
        self.main_container.pack(fill=BOTH, expand=YES)

        # 顶部配置区域
        top_frame = ttk.Frame(self.main_container)
        top_frame.pack(fill=X, pady=5)

        # 角色选择区域
        role_frame = ttk.LabelFrame(top_frame, text="角色选择", padding=10)
        role_frame.pack(side=LEFT, fill=X, expand=YES, padx=5, pady=5)

        ttk.Radiobutton(role_frame, text="服务端(被控制)", variable=self.role, value="server",
                        command=self.update_ui).pack(side=LEFT, padx=20, pady=5)
        ttk.Radiobutton(role_frame, text="客户端(控制端)", variable=self.role, value="client",
                        command=self.update_ui).pack(side=LEFT, padx=20, pady=5)

        # 性能设置区域
        performance_frame = ttk.LabelFrame(top_frame, text="性能设置", padding=10)
        performance_frame.pack(side=LEFT, fill=X, expand=YES, padx=5, pady=5)

        ttk.Label(performance_frame, text="图像质量:").pack(side=LEFT, padx=5, pady=5)
        quality_scale = ttk.Scale(
            performance_frame,
            variable=self.quality_level,
            from_=30,
            to=100,
            length=100,
            command=lambda v: self.quality_level.set(int(float(v)))
        )
        quality_scale.pack(side=LEFT, padx=5, pady=5)
        ttk.Label(performance_frame, textvariable=self.quality_level).pack(side=LEFT, padx=5, pady=5)

        ttk.Label(performance_frame, text="最大帧率:").pack(side=LEFT, padx=5, pady=5)
        fps_combobox = ttk.Combobox(
            performance_frame,
            textvariable=self.max_fps,
            values=[5, 10, 15, 20, 30],
            width=5
        )
        fps_combobox.pack(side=LEFT, padx=5, pady=5)

        # 连接配置区域
        conn_frame = ttk.LabelFrame(self.main_container, text="连接配置", padding=10)
        conn_frame.pack(fill=X, pady=5)

        conn_grid = ttk.Frame(conn_frame)
        conn_grid.pack(fill=X, expand=YES)

        # 服务端配置
        self.server_frame = ttk.Frame(conn_grid)
        self.server_frame.grid(row=0, column=0, columnspan=4, sticky=EW)

        ttk.Label(self.server_frame, text="监听端口:").pack(side=LEFT, padx=5, pady=5)
        self.port_entry = ttk.Entry(self.server_frame, width=10)
        self.port_entry.insert(0, str(self.port))
        self.port_entry.pack(side=LEFT, padx=5, pady=5)

        # 客户端配置
        self.client_frame = ttk.Frame(conn_grid)
        self.client_frame.grid(row=0, column=0, columnspan=4, sticky=EW)

        ttk.Label(self.client_frame, text="远程IP:").pack(side=LEFT, padx=5, pady=5)
        ttk.Entry(self.client_frame, textvariable=self.remote_ip, width=15).pack(side=LEFT, padx=5, pady=5)

        ttk.Label(self.client_frame, text="端口:").pack(side=LEFT, padx=5, pady=5)
        ttk.Entry(self.client_frame, textvariable=self.remote_port, width=10).pack(side=LEFT, padx=5, pady=5)

        # 控制按钮
        btn_frame = ttk.Frame(conn_frame)
        btn_frame.pack(side=RIGHT, anchor=N)

        self.start_btn = ttk.Button(
            btn_frame,
            text="启动服务",
            command=self.start_service,
            bootstyle=SUCCESS,
            width=12
        )
        self.start_btn.pack(pady=5)

        self.stop_btn = ttk.Button(
            btn_frame,
            text="停止服务",
            command=self.stop_service,
            bootstyle=DANGER,
            width=12,
            state=DISABLED
        )
        self.stop_btn.pack(pady=5)

        # 远程桌面显示区域（带滚动条）
        self.screen_frame = ttk.LabelFrame(self.main_container, text="远程桌面", padding=10)
        self.screen_frame.pack(fill=BOTH, expand=YES, pady=5)

        # 创建滚动条和画布
        self.scroll_frame = ttk.Frame(self.screen_frame)
        self.scroll_frame.pack(fill=BOTH, expand=YES)

        # 垂直滚动条
        self.vscrollbar = ttk.Scrollbar(self.scroll_frame, orient=VERTICAL)
        self.vscrollbar.pack(side=RIGHT, fill=Y)

        # 水平滚动条
        self.hscrollbar = ttk.Scrollbar(self.scroll_frame, orient=HORIZONTAL)
        self.hscrollbar.pack(side=BOTTOM, fill=X)

        # 创建画布用于显示远程桌面
        self.screen_canvas = tk.Canvas(
            self.scroll_frame,
            bg="black",
            cursor="arrow",
            yscrollcommand=self.vscrollbar.set,
            xscrollcommand=self.hscrollbar.set
        )
        self.screen_canvas.pack(side=LEFT, fill=BOTH, expand=YES)

        # 配置滚动条
        self.vscrollbar.config(command=self.on_vertical_scroll)
        self.hscrollbar.config(command=self.on_horizontal_scroll)

        # 绑定鼠标和键盘事件
        self.screen_canvas.bind("<Button-1>", self.send_mouse_click)
        self.screen_canvas.bind("<Button-3>", self.send_right_click)
        self.screen_canvas.bind("<B1-Motion>", self.send_mouse_drag)
        self.screen_canvas.bind("<Motion>", self.send_mouse_move)
        self.screen_canvas.bind("<MouseWheel>", self.send_mouse_scroll)
        self.screen_canvas.bind("<Key>", self.send_key_press)
        self.screen_canvas.focus_set()

        # 状态信息
        status_frame = ttk.LabelFrame(self.main_container, text="状态信息", padding=10)
        status_frame.pack(fill=X, pady=5)

        self.status_var = tk.StringVar(value="就绪 - 请选择角色并启动服务")
        ttk.Label(status_frame, textvariable=self.status_var).pack(side=LEFT, padx=10)

        self.fps_var = tk.StringVar(value="FPS: 0")
        ttk.Label(status_frame, textvariable=self.fps_var).pack(side=LEFT, padx=20)

        self.res_var = tk.StringVar(value="分辨率: 0x0")
        ttk.Label(status_frame, textvariable=self.res_var).pack(side=LEFT, padx=20)

        self.fullscreen_btn = ttk.Button(
            status_frame,
            text="全屏 (F11)",
            command=self.toggle_fullscreen,
            bootstyle=INFO
        )
        self.fullscreen_btn.pack(side=RIGHT, padx=10)

        # 日志区域
        log_frame = ttk.LabelFrame(self.main_container, text="操作日志", padding=10)
        log_frame.pack(fill=X, pady=5)

        self.log_text = scrolledtext.ScrolledText(
            log_frame,
            wrap=tk.WORD,
            height=4,
            font=('SimHei', 10)
        )
        self.log_text.pack(fill=X, pady=5)
        self.log_text.config(state=DISABLED)

        # 初始化UI状态
        self.update_ui()
        self.log_message("欢迎使用增强型远程桌面工具")
        self.log_message("请选择角色并配置连接参数")

        # 帧率计算变量
        self.frame_count = 0
        self.fps_start_time = time.time()
        self.fps_update_thread = threading.Thread(target=self.update_fps, daemon=True)
        self.fps_update_thread.start()

    def update_ui(self):
        """根据选择的角色更新UI"""
        role = self.role.get()
        if role == "server":
            self.server_frame.grid()
            self.client_frame.grid_remove()
            self.start_btn.config(text="启动服务")
            self.log_message("已切换到服务端模式")
        else:
            self.client_frame.grid()
            self.server_frame.grid_remove()
            self.start_btn.config(text="连接到服务端")
            self.log_message("已切换到客户端模式")

    def log_message(self, message):
        """在日志区域添加消息"""
        timestamp = datetime.now().strftime("%H:%M:%S")
        self.log_text.config(state=NORMAL)
        self.log_text.insert(END, f"[{timestamp}] {message}\n")
        self.log_text.see(END)  # 滚动到最新内容
        self.log_text.config(state=DISABLED)

    def update_status(self, message):
        """更新状态信息"""
        self.status_var.set(message)
        self.log_message(message)

    def update_fps(self):
        """更新帧率显示"""
        while True:
            time.sleep(1)  # 每秒更新一次
            if self.is_connected and self.role.get() == "client":
                elapsed = time.time() - self.fps_start_time
                if elapsed > 0:
                    fps = self.frame_count / elapsed
                    self.fps_var.set(f"FPS: {fps:.1f}")
                self.frame_count = 0
                self.fps_start_time = time.time()
            else:
                self.fps_var.set("FPS: 0")

    def start_service(self):
        """启动服务端或客户端"""
        if self.is_running:
            return

        try:
            if self.role.get() == "server":
                # 启动服务端
                self.port = int(self.port_entry.get())
                if not (1024 <= self.port <= 65535):
                    messagebox.showerror("参数错误", "端口号必须在1024-65535之间")
                    return

                self.is_running = True
                self.start_btn.config(state=DISABLED)
                self.stop_btn.config(state=NORMAL)
                self.update_status(f"服务端启动中，监听端口 {self.port}...")

                # 启动服务端线程
                self.connection_thread = threading.Thread(target=self.run_server, daemon=True)
                self.connection_thread.start()
            else:
                # 启动客户端
                remote_ip = self.remote_ip.get()
                remote_port = self.remote_port.get()

                if not remote_ip:
                    messagebox.showerror("参数错误", "请输入远程IP地址")
                    return

                if not (1024 <= remote_port <= 65535):
                    messagebox.showerror("参数错误", "端口号必须在1024-65535之间")
                    return

                self.is_running = True
                self.start_btn.config(state=DISABLED)
                self.stop_btn.config(state=NORMAL)
                self.update_status(f"正在连接到 {remote_ip}:{remote_port}...")

                # 启动客户端线程
                self.connection_thread = threading.Thread(
                    target=self.run_client,
                    args=(remote_ip, remote_port),
                    daemon=True
                )
                self.connection_thread.start()

        except Exception as e:
            self.update_status(f"启动失败: {str(e)}")

    def stop_service(self):
        """停止服务端或客户端"""
        self.is_running = False
        self.is_connected = False

        # 关闭socket
        if self.server_socket:
            try:
                self.server_socket.close()
            except:
                pass

        if self.client_socket:
            try:
                self.client_socket.close()
            except:
                pass

        # 重置UI状态
        self.start_btn.config(state=NORMAL)
        self.stop_btn.config(state=DISABLED)

        if self.role.get() == "server":
            self.update_status("服务端已停止")
        else:
            self.update_status("客户端已断开连接")

        # 清除屏幕
        self.screen_canvas.delete("all")
        self.screen_canvas.create_text(
            self.screen_canvas.winfo_width() // 2,
            self.screen_canvas.winfo_height() // 2,
            text="已断开连接",
            fill="white"
        )

        self.res_var.set("分辨率: 0x0")

    def run_server(self):
        """运行服务端逻辑"""
        try:
            # 创建服务器socket
            self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            self.server_socket.bind((self.host, self.port))
            self.server_socket.listen(1)
            self.server_socket.settimeout(1)

            self.update_status(f"服务端已启动，等待连接...")

            # 等待客户端连接
            while self.is_running:
                try:
                    client_socket, addr = self.server_socket.accept()
                    self.client_socket = client_socket
                    self.is_connected = True
                    self.update_status(f"已接受来自 {addr} 的连接")

                    # 发送初始屏幕信息
                    initial_screen = ImageGrab.grab()
                    self.screen_width, self.screen_height = initial_screen.size

                    # 启动屏幕捕获和发送线程
                    self.screen_thread = threading.Thread(target=self.capture_and_send_screen, daemon=True)
                    self.screen_thread.start()

                    # 启动接收输入线程
                    input_thread = threading.Thread(target=self.receive_input, daemon=True)
                    input_thread.start()

                    # 保持连接
                    while self.is_running and self.is_connected:
                        time.sleep(1)

                    self.client_socket.close()
                    self.update_status("连接已关闭")
                    break

                except socket.timeout:
                    continue
                except Exception as e:
                    if self.is_running:
                        self.update_status(f"连接错误: {str(e)}")
                    break

        except Exception as e:
            self.update_status(f"服务端错误: {str(e)}")
        finally:
            self.is_running = False
            self.is_connected = False
            self.start_btn.config(state=NORMAL)
            self.stop_btn.config(state=DISABLED)

    def run_client(self, ip, port):
        """运行客户端逻辑"""
        try:
            # 连接到服务端
            self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.client_socket.connect((ip, port))
            self.is_connected = True

            self.update_status(f"已成功连接到 {ip}:{port}")

            # 启动接收屏幕线程
            self.screen_thread = threading.Thread(target=self.receive_and_display_screen, daemon=True)
            self.screen_thread.start()

            # 保持连接
            while self.is_running and self.is_connected:
                time.sleep(1)

        except Exception as e:
            self.update_status(f"客户端错误: {str(e)}")
        finally:
            self.is_running = False
            self.is_connected = False
            self.start_btn.config(state=NORMAL)
            self.stop_btn.config(state=DISABLED)

    def capture_and_send_screen(self):
        """捕获屏幕并发送到客户端，使用增量更新提高效率"""
        try:
            # 计算帧间隔时间
            frame_interval = 1.0 / self.max_fps.get()

            while self.is_running and self.is_connected and self.client_socket:
                start_time = time.time()

                # 捕获当前屏幕
                current_screen = ImageGrab.grab()
                current_width, current_height = current_screen.size

                # 检查屏幕尺寸是否变化
                screen_changed = (current_width != self.screen_width or
                                  current_height != self.screen_height)
                self.screen_width, self.screen_height = current_width, current_height

                # 确定是否需要发送完整屏幕
                send_full_screen = (self.last_screen is None or screen_changed)

                if send_full_screen:
                    # 发送完整屏幕
                    self.last_screen = current_screen.copy()
                    buffer = io.BytesIO()
                    current_screen.save(buffer, format="JPEG", quality=self.quality_level.get())
                    buffer.seek(0)
                    image_data = buffer.read()

                    # 准备数据包：类型(1字节) + 数据
                    data = b'F' + image_data  # 'F' 表示完整帧
                else:
                    # 计算差异区域，只发送变化的部分
                    diff = ImageChops.difference(current_screen, self.last_screen)
                    bbox = diff.getbbox()

                    if bbox is None:
                        # 没有变化，跳过发送
                        elapsed = time.time() - start_time
                        if elapsed < frame_interval:
                            time.sleep(frame_interval - elapsed)
                        continue

                    # 更新最后一帧
                    self.last_screen = current_screen.copy()

                    # 提取差异区域
                    diff_region = current_screen.crop(bbox)

                    # 保存差异区域和边界框信息
                    buffer = io.BytesIO()
                    diff_region.save(buffer, format="JPEG", quality=self.quality_level.get())
                    buffer.seek(0)
                    image_data = buffer.read()

                    # 打包边界框信息 (x1, y1, x2, y2)
                    bbox_data = struct.pack('>iiii', *bbox)

                    # 准备数据包：类型(1字节) + 边界框(16字节) + 数据
                    data = b'P' + bbox_data + image_data  # 'P' 表示部分帧

                # 压缩数据
                compressed_data = zlib.compress(data)

                # 发送数据长度和数据
                data_size = len(compressed_data)
                try:
                    self.client_socket.sendall(struct.pack('>I', data_size) + compressed_data)
                except:
                    self.is_connected = False
                    break

                # 控制帧率
                elapsed = time.time() - start_time
                sleep_time = frame_interval - elapsed
                if sleep_time > 0:
                    time.sleep(sleep_time)

        except Exception as e:
            self.update_status(f"屏幕捕获错误: {str(e)}")
            self.is_connected = False

    def receive_and_display_screen(self):
        """接收并显示远程屏幕，支持增量更新"""
        try:
            while self.is_running and self.is_connected and self.client_socket:
                # 先接收数据长度
                data_size_bytes = self._recv_all(4)
                if not data_size_bytes:
                    break

                data_size = struct.unpack('>I', data_size_bytes)[0]

                # 接收图像数据
                compressed_data = self._recv_all(data_size)
                if not compressed_data:
                    break

                # 解压缩数据
                data = zlib.decompress(compressed_data)

                # 解析数据类型
                frame_type = data[0:1]
                frame_data = data[1:]

                if frame_type == b'F':
                    # 完整帧
                    image = Image.open(io.BytesIO(frame_data))
                    self.remote_screen = image.copy()
                    self.screen_width, self.screen_height = image.size
                    self.res_var.set(f"分辨率: {self.screen_width}x{self.screen_height}")
                elif frame_type == b'P':
                    # 部分帧，需要更新
                    if self.remote_screen is None:
                        continue

                    # 解析边界框
                    bbox_data = frame_data[0:16]
                    image_data = frame_data[16:]
                    bbox = struct.unpack('>iiii', bbox_data)

                    # 打开差异图像
                    diff_image = Image.open(io.BytesIO(image_data))

                    # 将差异区域粘贴到远程屏幕
                    self.remote_screen.paste(diff_image, bbox)
                    image = self.remote_screen.copy()
                else:
                    # 未知帧类型
                    continue

                # 转换为Tkinter可用的格式
                tk_image = ImageTk.PhotoImage(image=image)

                # 在画布上显示图像
                self.screen_canvas.delete("all")
                self.screen_canvas.config(scrollregion=(0, 0, self.screen_width, self.screen_height))
                self.screen_canvas.create_image(-self.scroll_x, -self.scroll_y, anchor=tk.NW, image=tk_image)

                # 保存引用，防止被垃圾回收
                self.screen_canvas.image = tk_image

                # 更新帧率计数
                self.frame_count += 1

        except Exception as e:
            self.update_status(f"接收屏幕错误: {str(e)}")
            self.is_connected = False

    def _recv_all(self, n):
        """确保接收指定长度的数据"""
        data = b''
        while len(data) < n:
            packet = self.client_socket.recv(n - len(data))
            if not packet:
                return None
            data += packet
        return data

    def on_vertical_scroll(self, *args):
        """处理垂直滚动事件"""
        if args[0] == 'moveto':
            # 计算滚动位置
            if self.screen_height > 0:
                self.scroll_y = float(args[1]) * self.screen_height
                self.screen_canvas.yview_moveto(args[1])
                # 重新绘制图像以反映新的滚动位置
                if hasattr(self.screen_canvas, 'image') and self.screen_canvas.image:
                    self.screen_canvas.delete("all")
                    self.screen_canvas.create_image(
                        -self.scroll_x,
                        -self.scroll_y,
                        anchor=tk.NW,
                        image=self.screen_canvas.image
                    )

    def on_horizontal_scroll(self, *args):
        """处理水平滚动事件"""
        if args[0] == 'moveto':
            # 计算滚动位置
            if self.screen_width > 0:
                self.scroll_x = float(args[1]) * self.screen_width
                self.screen_canvas.xview_moveto(args[1])
                # 重新绘制图像以反映新的滚动位置
                if hasattr(self.screen_canvas, 'image') and self.screen_canvas.image:
                    self.screen_canvas.delete("all")
                    self.screen_canvas.create_image(
                        -self.scroll_x,
                        -self.scroll_y,
                        anchor=tk.NW,
                        image=self.screen_canvas.image
                    )

    def send_mouse_click(self, event):
        """发送鼠标左键点击事件，考虑滚动偏移"""
        if self.is_connected and self.role.get() == "client" and self.screen_width > 0:
            # 计算远程屏幕上的实际坐标（考虑滚动偏移）
            canvas_width = self.screen_canvas.winfo_width()
            canvas_height = self.screen_canvas.winfo_height()

            # 计算缩放比例
            if canvas_width > 0 and canvas_height > 0:
                scale_x = self.screen_width / max(canvas_width, 1)
                scale_y = self.screen_height / max(canvas_height, 1)

                # 计算实际坐标（包含滚动偏移）
                x = int((event.x * scale_x) + self.scroll_x)
                y = int((event.y * scale_y) + self.scroll_y)

                # 确保坐标在有效范围内
                x = max(0, min(x, self.screen_width - 1))
                y = max(0, min(y, self.screen_height - 1))

                # 发送鼠标事件
                event_data = {
                    "type": "mouse_click",
                    "x": x,
                    "y": y,
                    "button": "left"
                }
                self.send_event(event_data)

    def send_right_click(self, event):
        """发送鼠标右键点击事件，考虑滚动偏移"""
        if self.is_connected and self.role.get() == "client" and self.screen_width > 0:
            # 计算远程屏幕上的实际坐标（考虑滚动偏移）
            canvas_width = self.screen_canvas.winfo_width()
            canvas_height = self.screen_canvas.winfo_height()

            if canvas_width > 0 and canvas_height > 0:
                scale_x = self.screen_width / max(canvas_width, 1)
                scale_y = self.screen_height / max(canvas_height, 1)

                x = int((event.x * scale_x) + self.scroll_x)
                y = int((event.y * scale_y) + self.scroll_y)

                x = max(0, min(x, self.screen_width - 1))
                y = max(0, min(y, self.screen_height - 1))

                # 发送鼠标事件
                event_data = {
                    "type": "mouse_click",
                    "x": x,
                    "y": y,
                    "button": "right"
                }
                self.send_event(event_data)

    def send_mouse_move(self, event):
        """发送鼠标移动事件，考虑滚动偏移"""
        if self.is_connected and self.role.get() == "client" and self.screen_width > 0:
            # 计算远程屏幕上的实际坐标
            canvas_width = self.screen_canvas.winfo_width()
            canvas_height = self.screen_canvas.winfo_height()

            if canvas_width > 0 and canvas_height > 0:
                scale_x = self.screen_width / max(canvas_width, 1)
                scale_y = self.screen_height / max(canvas_height, 1)

                x = int((event.x * scale_x) + self.scroll_x)
                y = int((event.y * scale_y) + self.scroll_y)

                x = max(0, min(x, self.screen_width - 1))
                y = max(0, min(y, self.screen_height - 1))

                # 发送鼠标事件
                event_data = {
                    "type": "mouse_move",
                    "x": x,
                    "y": y
                }
                self.send_event(event_data)

    def send_mouse_drag(self, event):
        """发送鼠标拖拽事件，考虑滚动偏移"""
        if self.is_connected and self.role.get() == "client" and self.screen_width > 0:
            # 计算远程屏幕上的实际坐标
            canvas_width = self.screen_canvas.winfo_width()
            canvas_height = self.screen_canvas.winfo_height()

            if canvas_width > 0 and canvas_height > 0:
                scale_x = self.screen_width / max(canvas_width, 1)
                scale_y = self.screen_height / max(canvas_height, 1)

                x = int((event.x * scale_x) + self.scroll_x)
                y = int((event.y * scale_y) + self.scroll_y)

                x = max(0, min(x, self.screen_width - 1))
                y = max(0, min(y, self.screen_height - 1))

                # 发送鼠标事件
                event_data = {
                    "type": "mouse_drag",
                    "x": x,
                    "y": y
                }
                self.send_event(event_data)

    def send_mouse_scroll(self, event):
        """发送鼠标滚轮事件"""
        if self.is_connected and self.role.get() == "client":
            # 发送鼠标滚轮事件
            event_data = {
                "type": "mouse_scroll",
                "dx": event.delta,
                "dy": event.delta
            }
            self.send_event(event_data)

    def send_key_press(self, event):
        """发送键盘事件"""
        if self.is_connected and self.role.get() == "client":
            # 发送键盘事件
            event_data = {
                "type": "key_press",
                "key": event.char,
                "keysym": event.keysym
            }
            self.send_event(event_data)

    def send_event(self, event_data):
        """发送事件数据到服务端"""
        try:
            if self.client_socket and self.is_connected:
                # 序列化事件数据
                data = pickle.dumps(event_data)
                # 压缩数据
                compressed_data = zlib.compress(data)
                # 发送数据长度和数据
                self.client_socket.sendall(struct.pack('>I', len(compressed_data)) + compressed_data)
        except Exception as e:
            self.update_status(f"发送事件错误: {str(e)}")
            self.is_connected = False

    def receive_input(self):
        """接收并处理客户端发送的输入事件"""
        try:
            while self.is_running and self.is_connected and self.client_socket:
                # 接收数据长度
                data_size_bytes = self._recv_all(4)
                if not data_size_bytes:
                    break

                data_size = struct.unpack('>I', data_size_bytes)[0]

                # 接收事件数据
                compressed_data = self._recv_all(data_size)
                if not compressed_data:
                    break

                # 解压缩并反序列化
                data = zlib.decompress(compressed_data)
                event_data = pickle.loads(data)

                # 处理事件
                self.process_event(event_data)

        except Exception as e:
            self.update_status(f"接收输入错误: {str(e)}")
            self.is_connected = False

    def process_event(self, event_data):
        """处理接收到的输入事件"""
        event_type = event_data.get("type")

        if event_type == "mouse_move":
            # 移动鼠标
            self.mouse.position = (event_data["x"], event_data["y"])

        elif event_type == "mouse_click":
            # 鼠标点击
            x, y = event_data["x"], event_data["y"]
            self.mouse.position = (x, y)

            # 模拟鼠标点击
            button = mouse.Button.left if event_data["button"] == "left" else mouse.Button.right
            self.mouse.click(button, 1)

        elif event_type == "mouse_drag":
            # 鼠标拖拽
            self.mouse.position = (event_data["x"], event_data["y"])

        elif event_type == "mouse_scroll":
            # 鼠标滚轮
            dx = event_data["dx"] / 120
            dy = event_data["dy"] / 120
            self.mouse.scroll(dx, dy)

        elif event_type == "key_press":
            # 键盘输入
            key = event_data["key"]
            keysym = event_data["keysym"]

            if key:
                self.keyboard.type(key)
            else:
                # 处理特殊键
                try:
                    key_obj = getattr(keyboard.Key, keysym)
                    self.keyboard.press(key_obj)
                    self.keyboard.release(key_obj)
                except:
                    pass

    def toggle_fullscreen(self, event=None):
        """切换全屏模式"""
        self.is_fullscreen = not self.is_fullscreen
        self.attributes("-fullscreen", self.is_fullscreen)

        if self.is_fullscreen:
            self.fullscreen_btn.config(text="退出全屏 (Esc)")
            self.update_status("已进入全屏模式，按Esc退出")
            # 隐藏滚动条
            self.vscrollbar.pack_forget()
            self.hscrollbar.pack_forget()
        else:
            self.fullscreen_btn.config(text="全屏 (F11)")
            self.update_status("已退出全屏模式")
            # 显示滚动条
            self.vscrollbar.pack(side=RIGHT, fill=Y)
            self.hscrollbar.pack(side=BOTTOM, fill=X)

    def exit_fullscreen(self, event=None):
        """退出全屏模式"""
        if self.is_fullscreen:
            self.toggle_fullscreen()

    def create_widgets(self):
        pass
